/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */#include"ImageContainer.h"#include<string.h> // for memcpy, memset#include"GLImages.h" // for SurfaceTextureImage#include"gfx2DGlue.h"#include"gfxPlatform.h" // for gfxPlatform#include"gfxUtils.h" // for gfxUtils#include"libyuv.h"#include"mozilla/RefPtr.h" // for already_AddRefed#include"mozilla/ipc/CrossProcessMutex.h" // for CrossProcessMutex, etc#include"mozilla/layers/CompositorTypes.h"#include"mozilla/layers/ImageBridgeChild.h" // for ImageBridgeChild#include"mozilla/layers/ImageClient.h" // for ImageClient#include"mozilla/layers/LayersMessages.h"#include"mozilla/layers/SharedPlanarYCbCrImage.h"#include"mozilla/layers/SharedRGBImage.h"#include"mozilla/layers/TextureClientRecycleAllocator.h"#include"mozilla/gfx/gfxVars.h"#include"nsISupportsUtils.h" // for NS_IF_ADDREF#include"YCbCrUtils.h" // for YCbCr conversions#include"gfx2DGlue.h"#include"mozilla/gfx/2D.h"#include"mozilla/CheckedInt.h"#ifdef XP_MACOSX#include"mozilla/gfx/QuartzSupport.h"#endif#ifdef XP_WIN#include"gfxWindowsPlatform.h"#include<d3d10_1.h>#include"mozilla/gfx/DeviceManagerDx.h"#include"mozilla/layers/D3D11YCbCrImage.h"#endifnamespacemozilla{namespacelayers{usingnamespacemozilla::ipc;usingnamespaceandroid;usingnamespacemozilla::gfx;Atomic<int32_t>Image::sSerialCounter(0);Atomic<uint32_t>ImageContainer::sGenerationCounter(0);RefPtr<PlanarYCbCrImage>ImageFactory::CreatePlanarYCbCrImage(constgfx::IntSize&aScaleHint,BufferRecycleBin*aRecycleBin){returnnewRecyclingPlanarYCbCrImage(aRecycleBin);}BufferRecycleBin::BufferRecycleBin():mLock("mozilla.layers.BufferRecycleBin.mLock")// This member is only valid when the bin is not empty and will be properly// initialized in RecycleBuffer, but initializing it here avoids static analysis// noise.,mRecycledBufferSize(0){}voidBufferRecycleBin::RecycleBuffer(UniquePtr<uint8_t[]>aBuffer,uint32_taSize){MutexAutoLocklock(mLock);if(!mRecycledBuffers.IsEmpty()&&aSize!=mRecycledBufferSize){mRecycledBuffers.Clear();}mRecycledBufferSize=aSize;mRecycledBuffers.AppendElement(Move(aBuffer));}UniquePtr<uint8_t[]>BufferRecycleBin::GetBuffer(uint32_taSize){MutexAutoLocklock(mLock);if(mRecycledBuffers.IsEmpty()||mRecycledBufferSize!=aSize)returnMakeUnique<uint8_t[]>(aSize);uint32_tlast=mRecycledBuffers.Length()-1;UniquePtr<uint8_t[]>result=Move(mRecycledBuffers[last]);mRecycledBuffers.RemoveElementAt(last);returnresult;}voidBufferRecycleBin::ClearRecycledBuffers(){MutexAutoLocklock(mLock);if(!mRecycledBuffers.IsEmpty()){mRecycledBuffers.Clear();}mRecycledBufferSize=0;}ImageContainerListener::ImageContainerListener(ImageContainer*aImageContainer):mLock("mozilla.layers.ImageContainerListener.mLock"),mImageContainer(aImageContainer){}ImageContainerListener::~ImageContainerListener(){}voidImageContainerListener::NotifyComposite(constImageCompositeNotification&aNotification){MutexAutoLocklock(mLock);if(mImageContainer){mImageContainer->NotifyComposite(aNotification);}}voidImageContainerListener::ClearImageContainer(){MutexAutoLocklock(mLock);mImageContainer=nullptr;}voidImageContainer::EnsureImageClient(){// If we're not forcing a new ImageClient, then we can skip this if we don't have an existing// ImageClient, or if the existing one belongs to an IPC actor that is still open.if(!mIsAsync){return;}if(mImageClient&&mImageClient->GetForwarder()->GetLayersIPCActor()->IPCOpen()){return;}RefPtr<ImageBridgeChild>imageBridge=ImageBridgeChild::GetSingleton();if(imageBridge){mImageClient=imageBridge->CreateImageClient(CompositableType::IMAGE,this);if(mImageClient){mAsyncContainerHandle=mImageClient->GetAsyncHandle();mNotifyCompositeListener=newImageContainerListener(this);}else{// It's okay to drop the async container handle since the ImageBridgeChild// is going to die anyway.mAsyncContainerHandle=CompositableHandle();mNotifyCompositeListener=nullptr;}}}ImageContainer::ImageContainer(Modeflag):mReentrantMonitor("ImageContainer.mReentrantMonitor"),mGenerationCounter(++sGenerationCounter),mPaintCount(0),mDroppedImageCount(0),mImageFactory(newImageFactory()),mRecycleBin(newBufferRecycleBin()),mIsAsync(flag==ASYNCHRONOUS),mCurrentProducerID(-1){if(flag==ASYNCHRONOUS){EnsureImageClient();}}ImageContainer::ImageContainer(constCompositableHandle&aHandle):mReentrantMonitor("ImageContainer.mReentrantMonitor"),mGenerationCounter(++sGenerationCounter),mPaintCount(0),mDroppedImageCount(0),mImageFactory(nullptr),mRecycleBin(nullptr),mIsAsync(true),mAsyncContainerHandle(aHandle),mCurrentProducerID(-1){MOZ_ASSERT(mAsyncContainerHandle);}ImageContainer::~ImageContainer(){if(mNotifyCompositeListener){mNotifyCompositeListener->ClearImageContainer();}if(mAsyncContainerHandle){if(RefPtr<ImageBridgeChild>imageBridge=ImageBridgeChild::GetSingleton()){imageBridge->ForgetImageContainer(mAsyncContainerHandle);}}}RefPtr<PlanarYCbCrImage>ImageContainer::CreatePlanarYCbCrImage(){ReentrantMonitorAutoEntermon(mReentrantMonitor);EnsureImageClient();if(mImageClient&&mImageClient->AsImageClientSingle()){returnnewSharedPlanarYCbCrImage(mImageClient);}returnmImageFactory->CreatePlanarYCbCrImage(mScaleHint,mRecycleBin);}RefPtr<SharedRGBImage>ImageContainer::CreateSharedRGBImage(){ReentrantMonitorAutoEntermon(mReentrantMonitor);EnsureImageClient();if(!mImageClient||!mImageClient->AsImageClientSingle()){returnnullptr;}returnnewSharedRGBImage(mImageClient);}voidImageContainer::SetCurrentImageInternal(constnsTArray<NonOwningImage>&aImages){ReentrantMonitorAutoEntermon(mReentrantMonitor);mGenerationCounter=++sGenerationCounter;if(!aImages.IsEmpty()){NS_ASSERTION(mCurrentImages.IsEmpty()||mCurrentImages[0].mProducerID!=aImages[0].mProducerID||mCurrentImages[0].mFrameID<=aImages[0].mFrameID,"frame IDs shouldn't go backwards");if(aImages[0].mProducerID!=mCurrentProducerID){mFrameIDsNotYetComposited.Clear();mCurrentProducerID=aImages[0].mProducerID;}elseif(!aImages[0].mTimeStamp.IsNull()){// Check for expired framesfor(auto&img:mCurrentImages){if(img.mProducerID!=aImages[0].mProducerID||img.mTimeStamp.IsNull()||img.mTimeStamp>=aImages[0].mTimeStamp){break;}if(!img.mComposited&&!img.mTimeStamp.IsNull()&&img.mFrameID!=aImages[0].mFrameID){mFrameIDsNotYetComposited.AppendElement(img.mFrameID);}}// Remove really old frames, assuming they'll never be composited.constuint32_tmaxFrames=100;if(mFrameIDsNotYetComposited.Length()>maxFrames){uint32_tdropFrames=mFrameIDsNotYetComposited.Length()-maxFrames;mDroppedImageCount+=dropFrames;mFrameIDsNotYetComposited.RemoveElementsAt(0,dropFrames);}}}nsTArray<OwningImage>newImages;for(uint32_ti=0;i<aImages.Length();++i){NS_ASSERTION(aImages[i].mImage,"image can't be null");NS_ASSERTION(!aImages[i].mTimeStamp.IsNull()||aImages.Length()==1,"Multiple images require timestamps");if(i>0){NS_ASSERTION(aImages[i].mTimeStamp>=aImages[i-1].mTimeStamp,"Timestamps must not decrease");NS_ASSERTION(aImages[i].mFrameID>aImages[i-1].mFrameID,"FrameIDs must increase");NS_ASSERTION(aImages[i].mProducerID==aImages[i-1].mProducerID,"ProducerIDs must be the same");}OwningImage*img=newImages.AppendElement();img->mImage=aImages[i].mImage;img->mTimeStamp=aImages[i].mTimeStamp;img->mFrameID=aImages[i].mFrameID;img->mProducerID=aImages[i].mProducerID;for(auto&oldImg:mCurrentImages){if(oldImg.mFrameID==img->mFrameID&&oldImg.mProducerID==img->mProducerID){img->mComposited=oldImg.mComposited;break;}}}mCurrentImages.SwapElements(newImages);}voidImageContainer::ClearImagesFromImageBridge(){ReentrantMonitorAutoEntermon(mReentrantMonitor);SetCurrentImageInternal(nsTArray<NonOwningImage>());}voidImageContainer::SetCurrentImages(constnsTArray<NonOwningImage>&aImages){MOZ_ASSERT(!aImages.IsEmpty());ReentrantMonitorAutoEntermon(mReentrantMonitor);if(mImageClient){if(RefPtr<ImageBridgeChild>imageBridge=ImageBridgeChild::GetSingleton()){imageBridge->UpdateImageClient(mImageClient,this);}}SetCurrentImageInternal(aImages);}voidImageContainer::ClearAllImages(){if(mImageClient){// Let ImageClient release all TextureClients. This doesn't return// until ImageBridge has called ClearCurrentImageFromImageBridge.if(RefPtr<ImageBridgeChild>imageBridge=ImageBridgeChild::GetSingleton()){imageBridge->FlushAllImages(mImageClient,this);}return;}ReentrantMonitorAutoEntermon(mReentrantMonitor);SetCurrentImageInternal(nsTArray<NonOwningImage>());}voidImageContainer::ClearCachedResources(){ReentrantMonitorAutoEntermon(mReentrantMonitor);if(mImageClient&&mImageClient->AsImageClientSingle()){if(!mImageClient->HasTextureClientRecycler()){return;}mImageClient->GetTextureClientRecycler()->ShrinkToMinimumSize();return;}returnmRecycleBin->ClearRecycledBuffers();}voidImageContainer::SetCurrentImageInTransaction(Image*aImage){AutoTArray<NonOwningImage,1>images;images.AppendElement(NonOwningImage(aImage));SetCurrentImagesInTransaction(images);}voidImageContainer::SetCurrentImagesInTransaction(constnsTArray<NonOwningImage>&aImages){NS_ASSERTION(NS_IsMainThread(),"Should be on main thread.");NS_ASSERTION(!mImageClient,"Should use async image transfer with ImageBridge.");SetCurrentImageInternal(aImages);}boolImageContainer::IsAsync()const{returnmIsAsync;}CompositableHandleImageContainer::GetAsyncContainerHandle(){NS_ASSERTION(IsAsync(),"Shared image ID is only relevant to async ImageContainers");NS_ASSERTION(mAsyncContainerHandle,"Should have a shared image ID");EnsureImageClient();returnmAsyncContainerHandle;}boolImageContainer::HasCurrentImage(){ReentrantMonitorAutoEntermon(mReentrantMonitor);return!mCurrentImages.IsEmpty();}voidImageContainer::GetCurrentImages(nsTArray<OwningImage>*aImages,uint32_t*aGenerationCounter){ReentrantMonitorAutoEntermon(mReentrantMonitor);*aImages=mCurrentImages;if(aGenerationCounter){*aGenerationCounter=mGenerationCounter;}}gfx::IntSizeImageContainer::GetCurrentSize(){ReentrantMonitorAutoEntermon(mReentrantMonitor);if(mCurrentImages.IsEmpty()){returngfx::IntSize(0,0);}returnmCurrentImages[0].mImage->GetSize();}voidImageContainer::NotifyComposite(constImageCompositeNotification&aNotification){ReentrantMonitorAutoEntermon(mReentrantMonitor);// An image composition notification is sent the first time a particular// image is composited by an ImageHost. Thus, every time we receive such// a notification, a new image has been painted.++mPaintCount;if(aNotification.producerID()==mCurrentProducerID){uint32_ti;for(i=0;i<mFrameIDsNotYetComposited.Length();++i){if(mFrameIDsNotYetComposited[i]<=aNotification.frameID()){if(mFrameIDsNotYetComposited[i]<aNotification.frameID()){++mDroppedImageCount;}}else{break;}}mFrameIDsNotYetComposited.RemoveElementsAt(0,i);for(auto&img:mCurrentImages){if(img.mFrameID==aNotification.frameID()){img.mComposited=true;}}}if(!aNotification.imageTimeStamp().IsNull()){mPaintDelay=aNotification.firstCompositeTimeStamp()-aNotification.imageTimeStamp();}}#ifdef XP_WIND3D11YCbCrRecycleAllocator*ImageContainer::GetD3D11YCbCrRecycleAllocator(KnowsCompositor*aAllocator){if(mD3D11YCbCrRecycleAllocator&&aAllocator==mD3D11YCbCrRecycleAllocator->GetAllocator()){returnmD3D11YCbCrRecycleAllocator;}RefPtr<ID3D11Device>device=gfx::DeviceManagerDx::Get()->GetContentDevice();if(!device){device=gfx::DeviceManagerDx::Get()->GetCompositorDevice();}LayersBackendbackend=aAllocator->GetCompositorBackendType();if(!device||backend!=LayersBackend::LAYERS_D3D11){returnnullptr;}RefPtr<ID3D10Multithread>multi;HRESULThr=device->QueryInterface((ID3D10Multithread**)getter_AddRefs(multi));if(FAILED(hr)||!multi){gfxWarning()<<"Multithread safety interface not supported. "<<hr;returnnullptr;}multi->SetMultithreadProtected(TRUE);mD3D11YCbCrRecycleAllocator=newD3D11YCbCrRecycleAllocator(aAllocator,device);returnmD3D11YCbCrRecycleAllocator;}#endifPlanarYCbCrImage::PlanarYCbCrImage():Image(nullptr,ImageFormat::PLANAR_YCBCR),mOffscreenFormat(SurfaceFormat::UNKNOWN),mBufferSize(0){}RecyclingPlanarYCbCrImage::~RecyclingPlanarYCbCrImage(){if(mBuffer){mRecycleBin->RecycleBuffer(Move(mBuffer),mBufferSize);}}size_tRecyclingPlanarYCbCrImage::SizeOfExcludingThis(MallocSizeOfaMallocSizeOf)const{// Ignoring:// - mData - just wraps mBuffer// - Surfaces should be reported under gfx-surfaces-*:// - mSourceSurface// - Base class:// - mImplData is not used// Not owned:// - mRecycleBinsize_tsize=aMallocSizeOf(mBuffer.get());// Could add in the future:// - mBackendData (from base class)returnsize;}UniquePtr<uint8_t[]>RecyclingPlanarYCbCrImage::AllocateBuffer(uint32_taSize){returnmRecycleBin->GetBuffer(aSize);}staticvoidCopyPlane(uint8_t*aDst,constuint8_t*aSrc,constgfx::IntSize&aSize,int32_taStride,int32_taSkip){if(!aSkip){// Fast path: planar input.memcpy(aDst,aSrc,aSize.height*aStride);}else{int32_theight=aSize.height;int32_twidth=aSize.width;for(inty=0;y<height;++y){constuint8_t*src=aSrc;uint8_t*dst=aDst;// Slow pathfor(intx=0;x<width;++x){*dst++=*src++;src+=aSkip;}aSrc+=aStride;aDst+=aStride;}}}boolRecyclingPlanarYCbCrImage::CopyData(constData&aData){mData=aData;// update buffer size// Use uint32_t throughout to match AllocateBuffer's param and mBufferSizeconstautocheckedSize=CheckedInt<uint32_t>(mData.mCbCrStride)*mData.mCbCrSize.height*2+CheckedInt<uint32_t>(mData.mYStride)*mData.mYSize.height;if(!checkedSize.isValid())returnfalse;constautosize=checkedSize.value();// get new buffermBuffer=AllocateBuffer(size);if(!mBuffer)returnfalse;// update buffer sizemBufferSize=size;mData.mYChannel=mBuffer.get();mData.mCbChannel=mData.mYChannel+mData.mYStride*mData.mYSize.height;mData.mCrChannel=mData.mCbChannel+mData.mCbCrStride*mData.mCbCrSize.height;CopyPlane(mData.mYChannel,aData.mYChannel,mData.mYSize,mData.mYStride,mData.mYSkip);CopyPlane(mData.mCbChannel,aData.mCbChannel,mData.mCbCrSize,mData.mCbCrStride,mData.mCbSkip);CopyPlane(mData.mCrChannel,aData.mCrChannel,mData.mCbCrSize,mData.mCbCrStride,mData.mCrSkip);mSize=aData.mPicSize;mOrigin=gfx::IntPoint(aData.mPicX,aData.mPicY);returntrue;}gfxImageFormatPlanarYCbCrImage::GetOffscreenFormat(){returnmOffscreenFormat==SurfaceFormat::UNKNOWN?gfxVars::OffscreenFormat():mOffscreenFormat;}boolPlanarYCbCrImage::AdoptData(constData&aData){mData=aData;mSize=aData.mPicSize;mOrigin=gfx::IntPoint(aData.mPicX,aData.mPicY);returntrue;}uint8_t*RecyclingPlanarYCbCrImage::AllocateAndGetNewBuffer(uint32_taSize){// get new buffermBuffer=AllocateBuffer(aSize);if(mBuffer){// update buffer sizemBufferSize=aSize;}returnmBuffer.get();}already_AddRefed<gfx::SourceSurface>PlanarYCbCrImage::GetAsSourceSurface(){if(mSourceSurface){RefPtr<gfx::SourceSurface>surface(mSourceSurface);returnsurface.forget();}gfx::IntSizesize(mSize);gfx::SurfaceFormatformat=gfx::ImageFormatToSurfaceFormat(GetOffscreenFormat());gfx::GetYCbCrToRGBDestFormatAndSize(mData,format,size);if(mSize.width>PlanarYCbCrImage::MAX_DIMENSION||mSize.height>PlanarYCbCrImage::MAX_DIMENSION){NS_ERROR("Illegal image dest width or height");returnnullptr;}RefPtr<gfx::DataSourceSurface>surface=gfx::Factory::CreateDataSourceSurface(size,format);if(NS_WARN_IF(!surface)){returnnullptr;}DataSourceSurface::ScopedMapmapping(surface,DataSourceSurface::WRITE);if(NS_WARN_IF(!mapping.IsMapped())){returnnullptr;}gfx::ConvertYCbCrToRGB(mData,format,size,mapping.GetData(),mapping.GetStride());mSourceSurface=surface;returnsurface.forget();}NVImage::NVImage():Image(nullptr,ImageFormat::NV_IMAGE),mBufferSize(0){}NVImage::~NVImage()=default;IntSizeNVImage::GetSize(){returnmSize;}IntRectNVImage::GetPictureRect(){returnmData.GetPictureRect();}already_AddRefed<SourceSurface>NVImage::GetAsSourceSurface(){if(mSourceSurface){RefPtr<gfx::SourceSurface>surface(mSourceSurface);returnsurface.forget();}// Convert the current NV12 or NV21 data to YUV420P so that we can follow the// logics in PlanarYCbCrImage::GetAsSourceSurface().constintbufferLength=mData.mYSize.height*mData.mYStride+mData.mCbCrSize.height*mData.mCbCrSize.width*2;auto*buffer=newuint8_t[bufferLength];DataaData=mData;aData.mCbCrStride=aData.mCbCrSize.width;aData.mCbSkip=0;aData.mCrSkip=0;aData.mYChannel=buffer;aData.mCbChannel=aData.mYChannel+aData.mYSize.height*aData.mYStride;aData.mCrChannel=aData.mCbChannel+aData.mCbCrSize.height*aData.mCbCrStride;if(mData.mCbChannel<mData.mCrChannel){// NV12libyuv::NV12ToI420(mData.mYChannel,mData.mYStride,mData.mCbChannel,mData.mCbCrStride,aData.mYChannel,aData.mYStride,aData.mCbChannel,aData.mCbCrStride,aData.mCrChannel,aData.mCbCrStride,aData.mYSize.width,aData.mYSize.height);}else{// NV21libyuv::NV21ToI420(mData.mYChannel,mData.mYStride,mData.mCrChannel,mData.mCbCrStride,aData.mYChannel,aData.mYStride,aData.mCbChannel,aData.mCbCrStride,aData.mCrChannel,aData.mCbCrStride,aData.mYSize.width,aData.mYSize.height);}// The logics in PlanarYCbCrImage::GetAsSourceSurface().gfx::IntSizesize(mSize);gfx::SurfaceFormatformat=gfx::ImageFormatToSurfaceFormat(gfxPlatform::GetPlatform()->GetOffscreenFormat());gfx::GetYCbCrToRGBDestFormatAndSize(aData,format,size);if(mSize.width>PlanarYCbCrImage::MAX_DIMENSION||mSize.height>PlanarYCbCrImage::MAX_DIMENSION){NS_ERROR("Illegal image dest width or height");returnnullptr;}RefPtr<gfx::DataSourceSurface>surface=gfx::Factory::CreateDataSourceSurface(size,format);if(NS_WARN_IF(!surface)){returnnullptr;}DataSourceSurface::ScopedMapmapping(surface,DataSourceSurface::WRITE);if(NS_WARN_IF(!mapping.IsMapped())){returnnullptr;}gfx::ConvertYCbCrToRGB(aData,format,size,mapping.GetData(),mapping.GetStride());mSourceSurface=surface;// Release the temporary buffer.delete[]buffer;returnsurface.forget();}boolNVImage::IsValid(){return!!mBufferSize;}uint32_tNVImage::GetBufferSize()const{returnmBufferSize;}NVImage*NVImage::AsNVImage(){returnthis;};boolNVImage::SetData(constData&aData){MOZ_ASSERT(aData.mCbSkip==1&&aData.mCrSkip==1);MOZ_ASSERT((int)std::abs(aData.mCbChannel-aData.mCrChannel)==1);// Calculate buffer size// Use uint32_t throughout to match AllocateBuffer's param and mBufferSizeconstautocheckedSize=CheckedInt<uint32_t>(aData.mYSize.height)*aData.mYStride+CheckedInt<uint32_t>(aData.mCbCrSize.height)*aData.mCbCrStride;if(!checkedSize.isValid())returnfalse;constautosize=checkedSize.value();// Allocate a new buffer.mBuffer=AllocateBuffer(size);if(!mBuffer){returnfalse;}// Update mBufferSize.mBufferSize=size;// Update mData.mData=aData;mData.mYChannel=mBuffer.get();mData.mCbChannel=mData.mYChannel+(aData.mCbChannel-aData.mYChannel);mData.mCrChannel=mData.mYChannel+(aData.mCrChannel-aData.mYChannel);// Update mSize.mSize=aData.mPicSize;// Copy the input data into mBuffer.// This copies the y-channel and the interleaving CbCr-channel.memcpy(mData.mYChannel,aData.mYChannel,mBufferSize);returntrue;}constNVImage::Data*NVImage::GetData()const{return&mData;}UniquePtr<uint8_t>NVImage::AllocateBuffer(uint32_taSize){UniquePtr<uint8_t>buffer(newuint8_t[aSize]);returnbuffer;}SourceSurfaceImage::SourceSurfaceImage(constgfx::IntSize&aSize,gfx::SourceSurface*aSourceSurface):Image(nullptr,ImageFormat::CAIRO_SURFACE),mSize(aSize),mSourceSurface(aSourceSurface),mTextureFlags(TextureFlags::DEFAULT){}SourceSurfaceImage::SourceSurfaceImage(gfx::SourceSurface*aSourceSurface):Image(nullptr,ImageFormat::CAIRO_SURFACE),mSize(aSourceSurface->GetSize()),mSourceSurface(aSourceSurface),mTextureFlags(TextureFlags::DEFAULT){}SourceSurfaceImage::~SourceSurfaceImage()=default;TextureClient*SourceSurfaceImage::GetTextureClient(KnowsCompositor*aForwarder){if(!aForwarder){returnnullptr;}autoentry=mTextureClients.LookupForAdd(aForwarder->GetSerial());if(entry){returnentry.Data();}RefPtr<TextureClient>textureClient;RefPtr<SourceSurface>surface=GetAsSourceSurface();MOZ_ASSERT(surface);if(surface){// gfx::BackendType::NONE means default to content backendtextureClient=TextureClient::CreateFromSurface(aForwarder,surface,BackendSelector::Content,mTextureFlags,ALLOC_DEFAULT);}if(textureClient){textureClient->SyncWithObject(aForwarder->GetSyncObject());entry.OrInsert([&textureClient](){returntextureClient;});returntextureClient;}// Remove the speculatively added entry.mTextureClients.Remove(aForwarder->GetSerial());returnnullptr;}ImageContainer::ProducerIDImageContainer::AllocateProducerID(){// Callable on all threads.staticAtomic<ImageContainer::ProducerID>sProducerID(0u);return++sProducerID;}}// namespace layers}// namespace mozilla